它是在 ECMAScript 6 (ES2015/ES6) 中引入的一项重要特性,旨在取代 CommonJS 和 AMD 规范,成为 JavaScript 模块化的主要标准。
ESM 模块的导入和导出遵循 ECMAScript 官方规范,与 CommonJS 不同。ESM 模块的导入使用 import
关键字,导出使用 export
关键字。
默认情况下 Node.js 会将 .js
后缀文件识别为 CJS
模块。
要让 Node.js 正确识别,主要有两种方式:
.mjs
作为文件后缀名 (例如 hello.mjs
);type
字段设置为 module
。json
{
"type":"module"
}
下面是同样 ESM 规范代码,后缀 .mjs
和 .js
的执行结果,前者能被正确执行,后者会报错,
接下来,我们就来详细的看看 ESM 模块的导入导出用法。
导入导出常见的主要有下面 3 种场景,下面分别介绍。
使用 export default
关键字。
js
// 文件 export_default.js
// 导出默认对象
export default {
// 定义 hello 方法,输出欢迎信息
hello(name) {
console.log(`Hello, ${name}!`)
},
// 定义 byebye 方法,输出道别信息
byebye(name) {
console.log(`byebye, ${name}!`)
},
// 定义 userInfo 对象,存储用户信息
userInfo: {
name: 'forever', // 用户名
age: 18 // 用户年龄
}
}
导入默认导出对象。
```js // 文件 import_default.js
// 引入 export_default.js 中默认导出的模块 import defaultModule from './export_default.js'
// 调用 defaultModule 中定义的 byebye() 方法,输出道别信息并传入用户姓名 defaultModule.byebye(defaultModule.userInfo.name)
// 调用 defaultModule 中定义的 hello() 方法,输出欢迎信息并传入用户姓名 defaultModule.hello(defaultModule.userInfo.name)
```
运行。
具名导出,使用 export
关键字。
```js // 文件 export.js
// 定义 hello 方法,输出欢迎信息
export function hello(name) {
console.log(Hello, ${name}!
)
}
// 定义 byebye 方法,输出道别信息
export function byebye(name) {
console.log(byebye, ${name}!
)
}
// 定义 userInfo 对象,存储用户信息 export const userInfo = { name: 'forever', // 用户名 age: 18 // 用户年龄 }
```
导入指定具名内容。
```js // 文件 import.js
// 引入 export_named.js 中具名导出的模块 import { byebye, hello, userInfo as user } from './export_named.js'
// 调用 byebye() 方法,输出道别信息并传入用户姓名 byebye(user.name)
// 调用 hello() 方法,输出欢迎信息并传入用户姓名 hello(user.name) ```
实用 as
关键字还可以修改导入内容的名称。
运行结果如下。
可以将另一个模块的内容直接全部导出。
导出同时也可以设置默认导出。
```js // 文件 export_all.js
// 从 export.js 中导出所有的模块成员 export * from './export.js'
// 导出一个默认模块,对象包含 goal 属性,初始值为 'learn' export default { goal: 'learn' } ```
使用 import * as xx from 'module'
导入所有内容。
```js // 文件 import_all.js
// 导入 export_all.js 中所有被导出的模块成员,并作为 allValues 对象的属性 import * as allValues from './export_all.js'
// 在控制台输出 allValues 对象 console.log(allValues)
// 从 allValues 对象中解构出 hello、byebye、default 和 userInfo 模块成员 const { hello, byebye, default: data, userInfo } = allValues
// 调用 hello() 方法,输出欢迎信息并传入用户姓名 hello(userInfo.name)
// 调用 byebye() 方法,输出道别信息并传入用户姓名 byebye(userInfo.name)
// 输出 data 对象的 goal 属性 console.log(data.goal) ```
下面是运行结果,
其中 export default
的内容,会出现在导入模块的 default
属性上。
某些时候有许多的,一个包内部可能有成百上千个 模块
,如果全都采用如下方式,显得不太优雅。
js
import { fun1, fun2 } from 'module_demo/hello.js'
import { fun3, fun4 } from 'module_demo/util.js'
import { fun5, fun6 } from 'module_demo/lib.js'
此时通常会将该模块内部需要导出的方法,统一收拢到一个 index.js
中,对外统一暴露。
下面是具体示例。
有 2 个文件 lib.js
和 util.js
,如下分别导出了一些方法。
``js
// lib.js
export function hello(name) {
console.log(
Hello, ${name}!`)
}
export default { filename: 'lib.js', des: 'lib.js的一些默认导出' } ```
``js
// util.js
export function byebye(name) {
console.log(
ByeBye, ${name}!`)
}
export default { filename: 'util.js', des: 'util.js的一些默认导出' } ```
此时通过 index.js
统一对外导出。
```js // 从 './lib.js' 中导出 hello 和默认导出并重命名为 libData export { hello, default as libData } from './lib.js'
// 从 './util.js' 中导出所有命名导出 export * from './util.js'
// 从 './util.js' 中默认导出并重命名为 utilData export { default as utilData } from './util.js' ```
使用的时候,就统一通过 index.js
引入即可。
```js // usage.js import { hello, byebye, libData, utilData } from './index.js'
hello(libData.filename) byebye(utilData.filename) ```
由于历史原因,大部分 Node 第三方模块还是 cjs
格式的 (近几年一些新包会同时提供 CJS 和 ESM 两种格式),
因此用户项目中也很少配置 type:"module"
,通常还是会通过构建工具将代码转换为 CJS 对外提供。
下面介绍几个上手比较简单的工具
测试代码也比较简单,如下。
export.js
``js
export function hello(name) {
console.log(
Hello, ${name}!`)
return name
}
export default { name: 'hello world' } ```
index.js
```js import defaultValue, { hello } from './export.js'
const result = hello(defaultValue.name)
export { result } ```
直接通过 npx
指令调用,或者先全局安装再使用 npm i -g tsup
。
sh
npx tsup packages/3/3.2/esm2cjs/index.js --format cjs
转换后的结果 dist/index.js
如下。
先通过 npm
全局安装。
sh
npm i -g @vercel/ncc
安装完后可以查看版本信息。
调用打包。
sh
ncc build packages/3/3.2/esm2cjs/index.js
tip:确保目标文件所在目录下的 package.json
中没有 type
字段。
打包后的结果如下。
当然上面针对 tsup
和 ncc
都只做了简单的介绍,更多用法还需查阅对应的文档。
除此之外常用的还可以使用 babel,Rollup,Webpack 等等这里就不再展开了,感兴趣的同学可以下去自行了解。
ES Modules (ESM) 模块系统是在 ECMAScript 6 (ES2015/ES6) 中引入的一项重要特性,旨在取代 CommonJS 和 AMD 规范,成为 JavaScript 模块化的主要标准。
为了使 Node.js 正确识别 ESM 模块,通常有 2 种方式:
.mjs
作为文件后缀名;package.json
中设置 type
字段为 module
。ES Modules 中的导入导出有多种用法,主要介绍了以下 4 种使用场景:
export default
,import xx from 'module'
);export xx
,import { xx } from 'module'
);export *
,import * as xx from 'module'
);export { xx } from 'module'
,export * from 'module'
)。最后介绍了 2 种将 ESM 模块转换为 CJS 模块的工具,tsup
和 ncc
。